package com.symaster.backups.frame.dialog;

import cn.hutool.core.io.IoUtil;
import com.symaster.backups.enumerate.PathType;
import com.symaster.backups.frame.element.collapsev2.CollapseItem;
import com.symaster.backups.frame.element.layout.TileLayout;
import com.symaster.backups.frame.element.scrollbar.Scrollbar;
import com.symaster.backups.model.BaseModel;
import com.symaster.backups.model.FileSystemMonitorBackupItemModel;
import com.symaster.backups.model.FileSystemMonitorModel;
import com.symaster.backups.repository.FileSystemMonitorBackupItemRepository;
import com.symaster.backups.repository.data2.FileSystemMonitorBackupData2;
import com.symaster.backups.service.SystemConfigService;
import com.symaster.common.swing.util.SwingUtil;
import com.symaster.common.util.IOUtil;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author yinmiao
 * @version 2024/2/21
 */
@Component
public class RollbackDialog extends JDialog {

    private final FileSystemMonitorBackupItemRepository fileSystemMonitorBackupItemRepository;
    private final FileSystemMonitorBackupData2 fileSystemMonitorBackupData2;
    private final JPanel root;
    private final Scrollbar scrollbar;
    private final SystemConfigService systemConfigService;
    private final JButton rollbackOk;
    private final Map<String, RollbackItem> rollbackMap = new Hashtable<>();

    private static final ThreadPoolExecutor loadPool = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1024));

    @Autowired
    public RollbackDialog(FileSystemMonitorBackupItemRepository fileSystemMonitorBackupItemRepository,
                          FileSystemMonitorBackupData2 fileSystemMonitorBackupData2,
                          SystemConfigService systemConfigService) {
        this.fileSystemMonitorBackupItemRepository = fileSystemMonitorBackupItemRepository;
        this.fileSystemMonitorBackupData2 = fileSystemMonitorBackupData2;
        this.systemConfigService = systemConfigService;

        JPanel jPanel = new JPanel();
        jPanel.setLayout(new BorderLayout(0, 10));
        setContentPane(jPanel);

        this.root = new JPanel();
        this.root.setBorder(new EmptyBorder(0, 0, 0, 5));
        this.root.setLayout(new TileLayout(0));

        this.scrollbar = new Scrollbar(this.root);
        jPanel.add(this.scrollbar, BorderLayout.CENTER);

        JPanel bar = new JPanel();
        bar.setLayout(new FlowLayout(FlowLayout.RIGHT, 15, 0));

        JButton cancelButton = new JButton("取消");
        cancelButton.addActionListener(e -> dispose());
        bar.add(cancelButton);

        rollbackOk = new JButton("恢复");
        rollbackOk.setEnabled(false);
        rollbackOk.addActionListener(e -> {

            StringBuilder sb = new StringBuilder("即将恢复以下文件 \n");
            rollbackMap.forEach((k, v) -> sb.append(k).append(" -> ").append(v.label).append("\n"));

            int i = JOptionPane.showConfirmDialog(this, sb.toString(), "提示", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE);
            if (0 == i) {

                List<Integer> collect = rollbackMap.values().stream().map(RollbackItem::getId).distinct().toList();
                Map<Integer, FileSystemMonitorBackupItemModel> byId = fileSystemMonitorBackupItemRepository
                        .findAllById(collect)
                        .stream()
                        .collect(Collectors.toMap(BaseModel::getId, e2 -> e2));

                String backupFolderRoot = systemConfigService.backupFolderRoot();

                rollbackMap.forEach((k, v) -> {
                    if (v.getId() != null) {
                        FileSystemMonitorBackupItemModel fileSystemMonitorBackupItemModel = byId.get(v.getId());
                        if (fileSystemMonitorBackupItemModel != null) {
                            Long lastModified = fileSystemMonitorBackupItemModel.getLastModified();
                            File file = new File(backupFolderRoot + File.separator + fileSystemMonitorBackupItemModel.getBackupFile());
                            if (!file.isFile()) {
                                JOptionPane.showMessageDialog(
                                        this,
                                        file.toPath() + "\n备份文件损坏, 无法恢复文件", "错误",
                                        JOptionPane.ERROR_MESSAGE
                                );
                                return;
                            }

                            File file1 = new File(k);
                            File parentFile = file1.getParentFile();
                            if (parentFile != null) {
                                if (!parentFile.isDirectory() && !parentFile.mkdirs()) {
                                    JOptionPane.showMessageDialog(
                                            this,
                                            parentFile.toPath() + "\n目标文件夹无法创建, 无法恢复文件", "错误",
                                            JOptionPane.ERROR_MESSAGE
                                    );
                                    return;
                                }
                            }

                            if (file1.isDirectory()) {
                                JOptionPane.showMessageDialog(
                                        this,
                                        file1.toPath() + "\n恢复目标路径为文件夹, 无法恢复文件", "错误",
                                        JOptionPane.ERROR_MESSAGE
                                );
                                return;
                            }

                            if (!file1.exists() || file1.isFile()) {
                                if (file1.exists() && !file1.delete()) {
                                    JOptionPane.showMessageDialog(
                                            this,
                                            file1.toPath() + "\n恢复目标文件删除失败, 请检查是否有权限", "错误",
                                            JOptionPane.ERROR_MESSAGE
                                    );
                                    return;
                                }

                                FileOutputStream fileOutputStream = null;
                                FileInputStream fileInputStream = null;
                                try {
                                    fileOutputStream = new FileOutputStream(file1);
                                    fileInputStream = new FileInputStream(file);
                                    IoUtil.copy(fileInputStream, fileOutputStream);
                                } catch (FileNotFoundException ex) {
                                    JOptionPane.showMessageDialog(
                                            this,
                                            file1.toPath() + "\n " + ex.getMessage(), "错误",
                                            JOptionPane.ERROR_MESSAGE
                                    );
                                    return;
                                } finally {
                                    IOUtil.close(fileInputStream, fileOutputStream);
                                }
                                if (!file1.setLastModified(v.getLastModified())) {
                                    System.out.println("setLastModified fail");
                                }
                            }
                        }
                    }

                });

                dispose();
            }
        });
        bar.add(rollbackOk);
        bar.setBorder(new EmptyBorder(5, 5, 5, 5));
        jPanel.add(bar, BorderLayout.SOUTH);
    }

    public void open(FileSystemMonitorModel f) {
        rollbackMap.clear();
        root.removeAll();

        loadPool.execute(() -> {

            fileSystemMonitorBackupItemRepository
                    .findFolder(f.getId())
                    .stream()
                    .sorted(sorted())
                    .map(e -> getCollapseItem(f, e, null))
                    .forEach(root::add);

            fileSystemMonitorBackupData2
                    .findFileGroup(f.getId())
                    .stream()
                    .sorted(sorted())
                    .map(e -> getCollapseItem(f, e, null))
                    .forEach(root::add);

            scrollbar.setPoint(0);
            refresh(this.scrollbar);
        });

        setVisible(true);
    }

    private CollapseItem getCollapseItem(FileSystemMonitorModel f, Object obj, Integer patentId) {

        FileSystemMonitorBackupItemModel folderModel;
        if (obj instanceof FileSystemMonitorBackupItemModel fileSystemMonitorBackupItemModel) {
            folderModel = fileSystemMonitorBackupItemModel;
        } else {
            folderModel = null;
        }

        JPanel centerPanel;
        if (folderModel != null && PathType.FOLDER == PathType.ofCode(folderModel.getPathType())) {
            centerPanel = new JPanel();
            centerPanel.setLayout(new TileLayout(0));
        } else {
            centerPanel = null;
        }

        Item item = new Item(obj, this, f.getId(), patentId);
        item.setBorder(new EmptyBorder(4, 0, 4, 0));
        CollapseItem collapseItem = new CollapseItem(item, centerPanel);
        collapseItem.setBorder(new EmptyBorder(0, 15, 0, 0));
        collapseItem.addRefreshListener(() -> refresh(this.scrollbar));
        collapseItem.addOpenListener(new CollapseItem.OpenListener() {
            @Override
            public void before(boolean o) {
                if (o && centerPanel != null) {

                    fileSystemMonitorBackupItemRepository
                            .findFolder(f.getId(), folderModel.getId())
                            .stream()
                            .sorted(sorted())
                            .map(e2 -> getCollapseItem(f, e2, folderModel.getId()))
                            .forEach(centerPanel::add);

                    fileSystemMonitorBackupData2
                            .findFileGroup(f.getId(), folderModel.getId())
                            .stream()
                            .sorted(sorted())
                            .map(e -> getCollapseItem(f, e, folderModel.getId()))
                            .forEach(centerPanel::add);
                }
            }

            @Override
            public void after(boolean o) {
                if (!o && centerPanel != null) {
                    centerPanel.removeAll();
                }
            }
        });

        return collapseItem;
    }

    private static Comparator<Object> sorted() {
        return (o1, o2) -> {
            if (o1 instanceof FileSystemMonitorBackupItemModel t1 &&
                    o2 instanceof FileSystemMonitorBackupItemModel t2) {
                if (t2.getPathType().equals(t1.getPathType())) {
                    return t1.getPathName().compareTo(t2.getPathName());
                }
                return Integer.compare(t2.getPathType(), t1.getPathType());
            } else if (o1 instanceof String t1 &&
                    o2 instanceof String t2) {
                return t1.compareTo(t2);
            } else {
                return 0;
            }
        };
    }

    private void refresh(Container parent) {
        parent.doLayout();
        parent.repaint();

        java.awt.Component[] components = parent.getComponents();
        if (components != null) {
            for (java.awt.Component component : components) {
                if (component instanceof Container container) {
                    refresh(container);
                }
            }
        }
    }

    private static class Item extends JComponent {

        private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        private final RollbackDialog rollbackDialog;
        private final Integer fileSystemMonitorId;
        private final Integer parentId;
        private final String pathName;
        private final PathType pathType;
        private String localPath;
        private Long localFileLastModified;

        public Item(Object obj,
                    RollbackDialog rollbackDialog,
                    Integer fileSystemMonitorId,
                    Integer parentId) {
            this.rollbackDialog = rollbackDialog;
            this.fileSystemMonitorId = fileSystemMonitorId;
            this.parentId = parentId;

            this.setLayout(new FlowLayout(FlowLayout.LEADING, 10, 0));
            this.setOpaque(false);

            if (obj instanceof FileSystemMonitorBackupItemModel f) {
                this.pathName = f.getPathName();
                this.pathType = PathType.ofCode(f.getPathType());
            } else if (obj instanceof String f) {
                this.pathName = f;
                this.pathType = PathType.FILE;
            } else {
                throw new IllegalArgumentException("Object 不支持");
            }

            this.add(getIconLabel());
            this.add(new JLabel(pathName));

            if (PathType.FILE == this.pathType) {
                List<FileSystemMonitorBackupItemModel> versions = findVersions();
                if (!versions.isEmpty()) {
                    this.localPath = rollbackDialog.systemConfigService.getSrcPath(versions.get(0));
                    if (StringUtils.isNotEmpty(this.localPath)) {
                        File file = new File(this.localPath);
                        if (file.isFile()) {
                            this.localFileLastModified = file.lastModified();
                        }
                    }
                }

                JComboBox<RollbackItem> stringJComboBox = new JComboBox<>();
                Font font = stringJComboBox.getFont();
                stringJComboBox.setFont(new Font(font.getFontName(), font.getStyle(), font.getSize() - 2));

                RollbackItem rollbackItem = new RollbackItem();
                rollbackItem.setLabel("不恢复");
                rollbackItem.setType(1);
                stringJComboBox.addItem(rollbackItem);

                Map<Integer, RollbackItem> versionsById = versions.stream().filter(e2 -> {
                    if (PathType.FOLDER == PathType.ofCode(e2.getPathType())) {
                        return true;
                    }
                    return localFileLastModified == null || !localFileLastModified.equals(e2.getLastModified());
                }).map(e2 -> {
                    RollbackItem rollbackItem1 = new RollbackItem();
                    rollbackItem1.setLabel(SDF.format(new Date(e2.getLastModified())));
                    rollbackItem1.setId(e2.getId());
                    rollbackItem1.setType(2);
                    rollbackItem1.setLastModified(e2.getLastModified());
                    return rollbackItem1;
                }).collect(Collectors.toMap(RollbackItem::getId, e2 -> e2));

                /* 将所有版本添加进下拉框 */
                versionsById.values()
                        .stream()
                        .sorted(Comparator.comparing(o -> o.label))
                        .forEach(stringJComboBox::addItem);

                /* 设置已选择的版本号 */
                RollbackItem selectRollbackItem;
                if (localPath != null && (selectRollbackItem = rollbackDialog.rollbackMap.get(localPath)) != null) {
                    if (selectRollbackItem.getId() != null) {
                        RollbackItem rollbackItem2 = versionsById.get(selectRollbackItem.getId());
                        if (rollbackItem2 != null) {
                            stringJComboBox.setSelectedItem(rollbackItem2);
                        }
                    }
                }

                /* 设置下拉框选择事件 */
                stringJComboBox.addActionListener(e -> {
                    if (localPath != null
                            && e.getSource() instanceof JComboBox<?> j
                            && j.getSelectedItem() instanceof RollbackItem select) {
                        if (select.getType() == 1) {
                            rollbackDialog.rollbackMap.remove(localPath);
                        } else {
                            rollbackDialog.rollbackMap.put(localPath, select);
                        }

                        /* 设置恢复按钮是否可用 */
                        rollbackDialog.rollbackOk.setEnabled(!rollbackDialog.rollbackMap.isEmpty());
                    }
                });

                add(stringJComboBox);
            }

            doLayout();
        }

        private JLabel getIconLabel() {
            JLabel iconLabel;
            if (PathType.FILE == pathType) {
                iconLabel = new JLabel(SwingUtil.loadIcon("file.png", 13, 13));
            } else {
                iconLabel = new JLabel(SwingUtil.loadIcon("folder.png", 13, 13));
            }

            iconLabel.setPreferredSize(new Dimension(20, 20));
            return iconLabel;
        }

        private List<FileSystemMonitorBackupItemModel> findVersions() {
            if (parentId == null) {
                return rollbackDialog.fileSystemMonitorBackupItemRepository.find(
                        fileSystemMonitorId,
                        pathName);
            } else {
                return rollbackDialog.fileSystemMonitorBackupItemRepository.find(
                        fileSystemMonitorId,
                        pathName,
                        parentId);
            }
        }
    }

    @Getter
    @Setter
    private static class RollbackItem {
        private String absolutePath;
        private String label;
        private Integer type;
        private Integer id;
        private Long lastModified;

        @Override
        public String toString() {
            return label;
        }
    }

}
